---
title: 'use cache: remote'
description: 'Learn how to use the `"use cache: remote"` directive to enable caching in dynamic contexts in your Next.js application.'
related:
  title: Related
  description: View related API references.
  links:
    - app/api-reference/directives/use-cache
    - app/api-reference/directives/use-cache-private
    - app/api-reference/config/next-config-js/cacheComponents
    - app/api-reference/config/next-config-js/cacheHandlers
    - app/api-reference/functions/cacheLife
    - app/api-reference/functions/cacheTag
    - app/api-reference/functions/connection
---

The `'use cache: remote'` directive enables caching of **shared data** in dynamic contexts where regular [`use cache`](/docs/app/api-reference/directives/use-cache) would not work, for example after calling [`await connection()`](/docs/app/api-reference/functions/connection), [`await cookies()`](/docs/app/api-reference/functions/cookies) or [`await headers()`](/docs/app/api-reference/functions/headers).

> **Good to know:**
>
> - Results are stored in server-side cache handlers and shared across all users.
> - For **user-specific data** that depends on [`await cookies()`](/docs/app/api-reference/functions/cookies) or [`await headers()`](/docs/app/api-reference/functions/headers), use [`'use cache: private'`](/docs/app/api-reference/directives/use-cache-private) instead.

## Usage

To use `'use cache: remote'`, enable the [`cacheComponents`](/docs/app/api-reference/config/next-config-js/cacheComponents) flag in your `next.config.ts` file:

```ts filename="next.config.ts" switcher
import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
  cacheComponents: true,
}

export default nextConfig
```

```js filename="next.config.js" switcher
/** @type {import('next').NextConfig} */
const nextConfig = {
  cacheComponents: true,
}

export default nextConfig
```

Then add `'use cache: remote'` to your function that needs to cache data in a dynamic context.

### Basic example

Cache product pricing that needs to be fetched at request time but can be shared across all users. Use [`cacheLife`](/docs/app/api-reference/functions/cacheLife#custom-cache-profiles) to set the cache lifetime of the price.

```tsx filename="app/product/[id]/page.tsx" switcher
import { Suspense } from 'react'
import { connection } from 'next/server'
import { cacheTag, cacheLife } from 'next/cache'

export default async function ProductPage({
  params,
}: {
  params: Promise<{ id: string }>
}) {
  const { id } = await params

  return (
    <div>
      <ProductDetails id={id} />
      <Suspense fallback={<div>Loading price...</div>}>
        <ProductPrice productId={id} />
      </Suspense>
    </div>
  )
}

function ProductDetails({ id }: { id: string }) {
  return <div>Product: {id}</div>
}

async function ProductPrice({ productId }: { productId: string }) {
  // Calling connection() makes this component dynamic, preventing
  // it from being included in the static shell. This ensures the price
  // is always fetched at request time.
  await connection()

  // Now we can cache the price in a remote cache handler.
  // Regular 'use cache' would NOT work here because we're in a dynamic context.
  const price = await getProductPrice(productId)

  return <div>Price: ${price}</div>
}

async function getProductPrice(productId: string) {
  'use cache: remote'
  cacheTag(`product-price-${productId}`)
  cacheLife({ expire: 3600 }) // 1 hour

  // This database query is cached and shared across all users
  return db.products.getPrice(productId)
}
```

```jsx filename="app/product/[id]/page.js" switcher
import { Suspense } from 'react'
import { connection } from 'next/server'
import { cacheTag, cacheLife } from 'next/cache'

export default async function ProductPage({ params }) {
  const { id } = await params

  return (
    <div>
      <ProductDetails id={id} />
      <Suspense fallback={<div>Loading price...</div>}>
        <ProductPrice productId={id} />
      </Suspense>
    </div>
  )
}

function ProductDetails({ id }) {
  return <div>Product: {id}</div>
}

async function ProductPrice({ productId }) {
  // Calling connection() makes this component dynamic, preventing
  // it from being included in the static shell. This ensures the price
  // is always fetched at request time.
  await connection()

  // Now we can cache the price in a remote cache handler.
  // Regular 'use cache' would NOT work here because we're in a dynamic context.
  const price = await getProductPrice(productId)

  return <div>Price: ${price}</div>
}

async function getProductPrice(productId) {
  'use cache: remote'
  cacheTag(`product-price-${productId}`)
  cacheLife({ expire: 3600 }) // 1 hour

  // This database query is cached and shared across all users
  return db.products.getPrice(productId)
}
```

> **Note:** Regular [`use cache`](/docs/app/api-reference/directives/use-cache) will not cache anything when used in a dynamic context (after [`await connection()`](/docs/app/api-reference/functions/connection), [`await cookies()`](/docs/app/api-reference/functions/cookies), [`await headers()`](/docs/app/api-reference/functions/headers), etc.). Use `'use cache: remote'` to enable runtime caching in these scenarios.

## How `use cache: remote` differs from `use cache` and `use cache: private`

Next.js provides three caching directives, each designed for different use cases:

| Feature                          | `use cache`                         | `'use cache: remote'`                                     | `'use cache: private'`              |
| -------------------------------- | ----------------------------------- | --------------------------------------------------------- | ----------------------------------- |
| **Works in dynamic context**     | No (requires static context)        | Yes (designed for dynamic contexts)                       | Yes                                 |
| **Access to `await cookies()`**  | No                                  | No                                                        | Yes                                 |
| **Access to `await headers()`**  | No                                  | No                                                        | Yes                                 |
| **After `await connection()`**   | No (won't cache)                    | No                                                        | No                                  |
| **Stored in cache handler**      | Yes (server-side)                   | Yes (server-side)                                         | No (client-side only)               |
| **Cache scope**                  | Global (shared)                     | Global (shared)                                           | Per-user (isolated)                 |
| **Supports runtime prefetching** | N/A (pre-rendered at build)         | No                                                        | Yes (when configured)               |
| **Use case**                     | Static, shared content (build-time) | Dynamic, shared content in runtime contexts (per-request) | Personalized, user-specific content |

> **Note:** While you can't call `await cookies()` or `await headers()` inside `'use cache: remote'`, you can read the values before calling a function that is wrapped by `'use cache: remote'` and the arguments will be included in the cache key. Note that this is not recommended as it will dramatically increase the cache size and reduce the cache hit rate.

### When to use each directive

Choose the right caching directive based on your use case:

**Use [`use cache`](/docs/app/api-reference/directives/use-cache) when:**

- Content can be prerendered at build time
- Content is shared across all users
- Content doesn't depend on request-specific data

**Use `'use cache: remote'` when:**

- You need caching within dynamic context
- Content is shared across users but must be rendered per-request (after `await connection()`)
- You want to cache expensive operations in a server-side cache handler

**Use [`'use cache: private'`](/docs/app/api-reference/directives/use-cache-private) when:**

- Content is personalized per-user (depends on cookies, headers)
- You need [runtime prefetching](/docs/app/guides/prefetching) of user-specific content
- Content should never be shared between users

## How it works

The `'use cache: remote'` directive enables runtime caching of shared data in dynamic contexts by storing results in server-side cache handlers rather than prerendering at build time.

### Dynamic context detection

When Next.js encounters certain APIs like [`connection()`](/docs/app/api-reference/functions/connection), [`cookies()`](/docs/app/api-reference/functions/cookies), or [`headers()`](/docs/app/api-reference/functions/headers), the context becomes "dynamic". In a dynamic context:

1. **Regular `use cache` stops working** - it won't cache anything
2. **`'use cache: remote'` continues to work** - it is cached by a remote cache handler.
3. **Results are stored server-side** in a key-value store configured for your deployment
4. **Cached data is shared across requests** - reducing database load and origin requests

> **Good to know:** Without `'use cache: remote'`, functions in dynamic contexts would execute on every request, potentially creating performance bottlenecks. Remote caching eliminates this issue by storing results in server-side cache handlers.

### Storage behavior

Remote caches are **persisted using server-side cache handlers**, which may include:

- **Distributed key-value stores** (in-memory or persistent storage solutions)
- **File system or in-memory storage** (often used in development or for custom deployments)
- **Environment-specific caches** (provided by your hosting infrastructure)
- **Custom or configured cache handlers** (depending on your application's setup)

This means:

1. Cached data is shared across all users and requests
2. Cache entries persist beyond a single session
3. Cache invalidation works via [`cacheTag`](/docs/app/api-reference/functions/cacheTag) and [`revalidateTag`](/docs/app/api-reference/functions/revalidateTag)
4. Cache expiration is controlled by [`cacheLife`](/docs/app/api-reference/functions/cacheLife) configuration

### Dynamic context example

```tsx
async function UserDashboard() {
  // Calling connection() makes the context dynamic
  await connection()

  // Without any caching directive, this runs on every request
  const stats = await getStats()

  // With 'use cache: remote', this is cached in the remote handler
  const analytics = await getAnalytics()

  return (
    <div>
      <Stats data={stats} />
      <Analytics data={analytics} />
    </div>
  )
}

async function getAnalytics() {
  'use cache: remote'
  cacheLife({ expire: 300 }) // 5 minutes

  // This expensive operation is cached and shared across all requests
  return fetchAnalyticsData()
}
```

## Request APIs and remote caches

While `'use cache: remote'` technically allows access to request-specific data by calling API's like [`cookies()`](/docs/app/api-reference/functions/cookies) and [`headers()`](/docs/app/api-reference/functions/headers) before calling a function that is wrapped by `'use cache: remote'`, it's generally not recommended to use them together:

| API                                                                                   | Allowed in `use cache` | Allowed in `'use cache: remote'` | Recommended                                                                                |
| ------------------------------------------------------------------------------------- | ---------------------- | -------------------------------- | ------------------------------------------------------------------------------------------ |
| [`cookies()`](/docs/app/api-reference/functions/cookies)                              | No                     | No                               | Use [`'use cache: private'`](/docs/app/api-reference/directives/use-cache-private) instead |
| [`headers()`](/docs/app/api-reference/functions/headers)                              | No                     | No                               | Use [`'use cache: private'`](/docs/app/api-reference/directives/use-cache-private) instead |
| [`connection()`](/docs/app/api-reference/functions/connection)                        | No                     | No                               | No - these cannot ever be cached                                                           |
| [`searchParams`](/docs/app/api-reference/file-conventions/page#searchparams-optional) | No                     | No                               | Use [`'use cache: private'`](/docs/app/api-reference/directives/use-cache-private) instead |

> **Important:** If you need to cache based on cookies, headers, or search params, use [`'use cache: private'`](/docs/app/api-reference/directives/use-cache-private) instead. Remote caches are shared across all users, so caching user-specific data in them can lead to incorrect results being served to different users.

## Nesting rules

Remote caches have specific nesting rules:

- Remote caches **can** be nested inside other remote caches (`'use cache: remote'`)
- Remote caches **can** be nested inside regular caches (`'use cache'`)
- Remote caches **cannot** be nested inside private caches (`'use cache: private'`)
- Private caches **cannot** be nested inside remote caches

```tsx
// VALID: Remote inside remote
async function outerRemote() {
  'use cache: remote'
  const result = await innerRemote()
  return result
}

async function innerRemote() {
  'use cache: remote'
  return getData()
}

// VALID: Remote inside regular cache
async function outerCache() {
  'use cache'
  // If this is in a dynamic context, the inner remote cache will work
  const result = await innerRemote()
  return result
}

async function innerRemote() {
  'use cache: remote'
  return getData()
}

// INVALID: Remote inside private
async function outerPrivate() {
  'use cache: private'
  const result = await innerRemote() // Error!
  return result
}

async function innerRemote() {
  'use cache: remote'
  return getData()
}

// INVALID: Private inside remote
async function outerRemote() {
  'use cache: remote'
  const result = await innerPrivate() // Error!
  return result
}

async function innerPrivate() {
  'use cache: private'
  return getData()
}
```

## Examples

The following examples demonstrate common patterns for using `'use cache: remote'`. For details about `cacheLife` parameters (`stale`, `revalidate`, `expire`), see the [`cacheLife` API reference](/docs/app/api-reference/functions/cacheLife).

### Per-request database queries

Cache expensive database queries that are accessed in dynamic contexts, reducing load on your database:

```tsx filename="app/dashboard/page.tsx"
import { connection } from 'next/server'
import { cacheLife, cacheTag } from 'next/cache'

export default async function DashboardPage() {
  // Make context dynamic
  await connection()

  const stats = await getGlobalStats()

  return <StatsDisplay stats={stats} />
}

async function getGlobalStats() {
  'use cache: remote'
  cacheTag('global-stats')
  cacheLife({ expire: 60 }) // 1 minute

  // This expensive database query is cached and shared across all users,
  // reducing load on your database
  const stats = await db.analytics.aggregate({
    total_users: 'count',
    active_sessions: 'count',
    revenue: 'sum',
  })

  return stats
}
```

### API responses in streaming contexts

Cache API responses that are fetched during streaming or after dynamic operations:

```tsx filename="app/feed/page.tsx"
import { Suspense } from 'react'
import { connection } from 'next/server'
import { cacheLife, cacheTag } from 'next/cache'

export default async function FeedPage() {
  return (
    <div>
      <Suspense fallback={<Skeleton />}>
        <FeedItems />
      </Suspense>
    </div>
  )
}

async function FeedItems() {
  // Dynamic context
  await connection()

  const items = await getFeedItems()

  return items.map((item) => <FeedItem key={item.id} item={item} />)
}

async function getFeedItems() {
  'use cache: remote'
  cacheTag('feed-items')
  cacheLife({ expire: 120 }) // 2 minutes

  // This API call is cached, reducing requests to your external service
  const response = await fetch('https://api.example.com/feed')
  return response.json()
}
```

### Computed data after dynamic checks

Cache expensive computations that occur after dynamic security or feature checks:

```tsx filename="app/reports/page.tsx"
import { connection } from 'next/server'
import { cacheLife } from 'next/cache'

export default async function ReportsPage() {
  // Dynamic security check
  await connection()

  const report = await generateReport()

  return <ReportViewer report={report} />
}

async function generateReport() {
  'use cache: remote'
  cacheLife({ expire: 3600 }) // 1 hour

  // This expensive computation is cached and shared across all authorized users,
  // avoiding repeated calculations
  const data = await db.transactions.findMany()

  return {
    totalRevenue: calculateRevenue(data),
    topProducts: analyzeProducts(data),
    trends: calculateTrends(data),
  }
}
```

### Mixed caching strategies

Combine static, remote, and private caching for optimal performance:

```tsx filename="app/product/[id]/page.tsx"
import { Suspense } from 'react'
import { connection } from 'next/server'
import { cookies } from 'next/headers'
import { cacheLife, cacheTag } from 'next/cache'

// Static product data - prerendered at build time
async function getProduct(id: string) {
  'use cache'
  cacheTag(`product-${id}`)

  // This is cached at build time and shared across all users
  return db.products.find({ where: { id } })
}

// Shared pricing data - cached at runtime in remote handler
async function getProductPrice(id: string) {
  'use cache: remote'
  cacheTag(`product-price-${id}`)
  cacheLife({ expire: 300 }) // 5 minutes

  // This is cached at runtime and shared across all users
  return db.products.getPrice({ where: { id } })
}

// User-specific recommendations - private cache per user
async function getRecommendations(productId: string) {
  'use cache: private'
  cacheLife({ expire: 60 }) // 1 minute

  const sessionId = (await cookies()).get('session-id')?.value

  // This is cached per-user and never shared
  return db.recommendations.findMany({
    where: { productId, sessionId },
  })
}

export default async function ProductPage({ params }) {
  const { id } = await params

  // Static product data
  const product = await getProduct(id)

  return (
    <div>
      <ProductDetails product={product} />

      {/* Dynamic shared price */}
      <Suspense fallback={<PriceSkeleton />}>
        <ProductPriceComponent productId={id} />
      </Suspense>

      {/* Dynamic personalized recommendations */}
      <Suspense fallback={<RecommendationsSkeleton />}>
        <ProductRecommendations productId={id} />
      </Suspense>
    </div>
  )
}

function ProductDetails({ product }) {
  return (
    <div>
      <h1>{product.name}</h1>
      <p>{product.description}</p>
    </div>
  )
}

async function ProductPriceComponent({ productId }) {
  // Make this component dynamic
  await connection()

  const price = await getProductPrice(productId)
  return <div>Price: ${price}</div>
}

async function ProductRecommendations({ productId }) {
  const recommendations = await getRecommendations(productId)
  return <RecommendationsList items={recommendations} />
}

function PriceSkeleton() {
  return <div>Loading price...</div>
}

function RecommendationsSkeleton() {
  return <div>Loading recommendations...</div>
}

function RecommendationsList({ items }) {
  return (
    <ul>
      {items.map((item) => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  )
}
```

> **Good to know**:
>
> - Remote caches are stored in server-side cache handlers and shared across all users
> - Remote caches work in dynamic contexts where regular [`use cache`](/docs/app/api-reference/directives/use-cache) would fail
> - Use [`cacheTag()`](/docs/app/api-reference/functions/cacheTag) and [`revalidateTag()`](/docs/app/api-reference/functions/revalidateTag) to invalidate remote caches on-demand
> - Use [`cacheLife()`](/docs/app/api-reference/functions/cacheLife) to configure cache expiration
> - For user-specific data, use [`'use cache: private'`](/docs/app/api-reference/directives/use-cache-private) instead of `'use cache: remote'`
> - Remote caches reduce origin load by storing computed or fetched data server-side

## Platform Support

| Deployment Option                                                   | Supported |
| ------------------------------------------------------------------- | --------- |
| [Node.js server](/docs/app/getting-started/deploying#nodejs-server) | Yes       |
| [Docker container](/docs/app/getting-started/deploying#docker)      | Yes       |
| [Static export](/docs/app/getting-started/deploying#static-export)  | No        |
| [Adapters](/docs/app/getting-started/deploying#adapters)            | Yes       |

## Version History

| Version   | Changes                                                             |
| --------- | ------------------------------------------------------------------- |
| `v16.0.0` | `"use cache: remote"` is enabled with the Cache Components feature. |
